import scipy
import copy
import os

this_dir, this_file = os.path.split(__file__)
eye_cup_image_fullpath = os.path.join(this_dir,'circle_512.gif')

class Eye_cup():
    """
    A class which will represent an eye cup.
    """
    def __init__(self, streak, cup, nerve):
        self.streak      = list(copy.deepcopy(streak))
        self.cup         = list(copy.deepcopy(cup))
        self.nerve       = list(copy.deepcopy(nerve))
        self.update_opacity(0.0)
        self.preped_for_drawing = False
        self._myWin = None

    def __eq__(self, other):
        if type(other) == type(None):
            return False
        if self.streak == other.streak and self.cup == other.cup and self.nerve == other.nerve:
            return True
        return False
    def __ne__(self, other):
        return not self == other

    @classmethod
    def default(self):
        streak      = [0, 0, 0.0]
        cup         = [0, 0, 400]
        nerve       = [0, 0, 40]
        return Eye_cup(streak, cup, nerve)

    @classmethod
    def from_eye_cup_file(self, filename='eye_cup.txt'):
        # -------------------------------------------------
        # -- read in any existing eye_cup.txt file:
        # -------------------------------------------------
        streak, cup, nerve = read_eye_cup_file(filename=filename)
        return Eye_cup( streak, cup, nerve)

    def prep_for_drawing(self, myWin, shade=1.0, opacity=None, 
                         rf=None, force=False):
        '''
        Prepare the eye cup object to call its draw method.
        Inputs:
            myWin                   : a psychopy window
            shade                   : 1.0 for black(fg), white(bg)
                                        will use self.shade if exists and not 'forced'
            opacity                 : how opaque the eye_cup should be
                                        will use self.opacity if exists and not 'forced'
            rf                      : If not None, then this is the RF object describing
                                        rf coordinates.
            force                   : prep for drawing even if already preped.
        Alters:
            self.vs
            self.ec
            self.on
        '''
        if opacity is None:
            opacity = self.opacity 
        #If the window has changed somehow, make sure we reprep for drawing
        if self._myWin is not myWin:
            force = True

        if (not self.preped_for_drawing) or force:
            # if self.opacity is set, use that instead of defaults, unless forced.
            if hasattr(self, 'opacity') and not force:
                opacity = self.opacity

            if hasattr(self, 'bg_shade') and not force:
                bg_shade = self.bg_shade

            self.do_prep(myWin, shade, opacity, rf)
            self.preped_for_drawing = True
        

    def do_prep(self, myWin, shade, opacity, rf):
        from psychopy import visual
        from PatchGroup import PatchGroup
        import copy
        
        # shorter names makes for easier to read code.
        streak      = copy.deepcopy(self.streak)
        cup         = copy.deepcopy(self.cup)
        nerve       = copy.deepcopy(self.nerve)

        if rf is not None:
            # already in RF coords, so translate back to screen coords
            size   = rf.size
            center = rf.center
            units  = 'norm'
            # conversion factor
            pixels_to_units = 1/(size[0]*1.0)
            # convert units
            streak[0]   = (-center[0]+streak[0])*pixels_to_units 
            streak[1]   = (-center[1]+streak[1])*pixels_to_units 
            cup[0]      = (-center[0]+cup[0])   *pixels_to_units 
            cup[1]      = (-center[1]+cup[1])   *pixels_to_units 
            cup[2]      = cup[2]                *pixels_to_units 
            nerve[0]    = (-center[0]+nerve[0]) *pixels_to_units 
            nerve[1]    = (-center[1]+nerve[1]) *pixels_to_units 
            nerve[2]    = nerve[2]              *pixels_to_units 
            crosshair_width = 80                *pixels_to_units
        else:
            units = 'pix'
            crosshair_width = 80 # width of crosshairs in pixels
            
        # -------------------------------------------------------------------------------
        # --                         WHITE BACKGROUND
        # -------------------------------------------------------------------------------
        self.white = visual.PatchStim( myWin, pos=(0.0,0.0), size=(4000,4000),
                                  tex=scipy.ones((8,8))*shade,
                                  units = units, opacity = 1.0 - opacity)
        # -------------------------------------------------------------------------------
        # --                         VISUAL STREAK
        # -------------------------------------------------------------------------------
        self.vs = PatchGroup()
        # make the horizontal part of the crosshairs
        self.vs.add_patch( 
                     visual.PatchStim( myWin, pos=(0.0,0.0),
                                       size=(4000,crosshair_width),
                                       tex=scipy.ones((8,8))*-shade, units = units) )
        self.vs.add_patch( 
                     visual.PatchStim( myWin, pos=(0.0,2000.0),
                                       size=(4000,4000),
                                       tex=scipy.ones((8,8))*shade,
                                       rgb=(-1, -1, 1),  units = units) )
        # -------------------------------------------------------------------------------
        # --                         EYE CUP
        # -------------------------------------------------------------------------------
        self.ec = PatchGroup()
        # make the outer edge of the eyecup (sets color of border)
        self.ec.add_patch(      
                     visual.PatchStim( myWin, pos=(0.0,0.0),                        \
                                       size=(cup[2], cup[2]),                       \
                                       mask=eye_cup_image_fullpath,
                                       tex=scipy.ones((8,8))*shade,  \
                                       units = units, rgb = (-1.0,-1.0,-1.0) )      \
                    )
        # make the inner circle of the eyecup (sets inner color)
        self.ec.add_patch(      
                     visual.PatchStim( myWin, pos=(0.0,0.0),                        \
                                       size=(cup[2]*0.9, cup[2]*0.9),               \
                                       mask=eye_cup_image_fullpath,
                                       tex=scipy.ones((8,8))*shade,  \
                                       units = units,  rgb = (1.0,-1.0,-1.0) )      \
                    )
        self.ec.can_rotate(False)
        # -------------------------------------------------------------------------------
        # --                         OPTIC NERVE
        # -------------------------------------------------------------------------------
        self.on = PatchGroup()
        # make the outer edge of the eyecup (sets color of border)
        self.on.add_patch(
                     visual.PatchStim( myWin, pos=(0.0,0.0 ),                         \
                                       size=(nerve[2], nerve[2]), mask='circle',      \
                                       tex=scipy.ones((8,8))*-shade, units = units )  \
                    )
        self.on.can_rotate(False)
        # we have to unrotate things and then move them.
        if rf is not None:
            self.vs.rotate('cc', rf.ori)
            self.on.rotate('cc', rf.ori)
            self.ec.rotate('cc', rf.ori)
      
        # move things into place.
        self.vs.move( 'up',    streak[1])
        self.vs.move( 'right', streak[0])
        self.on.move( 'up',    nerve[1])
        self.on.move( 'right', nerve[0])
        self.ec.move( 'up',    cup[1])
        self.ec.move( 'right', cup[0])
        # rotate things appropriately
        self.vs.rotate( 'c', streak[2] )

        # remember that this has been set up for drawing
        self._myWin = myWin
        self.preped_for_drawing = True

    def update_bg_shade(self, bg_shade):
        self.bg_shade = bg_shade
        if hasattr(self, 'white'):
            self.white.setTex(scipy.ones((8,8))*self.bg_shade)
            self.vs.patches[0].setTex(scipy.ones((8,8))*-self.bg_shade)
            self.vs.patches[1].setTex(scipy.ones((8,8))*self.bg_shade)
            self.ec.patches[0].setTex(scipy.ones((8,8))*self.bg_shade)
            self.ec.patches[1].setTex(scipy.ones((8,8))*self.bg_shade)
            self.on.patches[0].setTex(scipy.ones((8,8))*-self.bg_shade)

    def update_opacity(self, opacity):
        self.opacity = opacity
        if hasattr(self, 'white'):
            self.white.setOpacity(1.0 - self.opacity)

    def draw(self, focus=None):
        """
            focus                   : draws the specified layer clearly ( one of 'vs', 'ec', 'on' )
        """
        self.vs.draw()
        self.ec.draw()
        self.on.draw()
        self.white.draw()
        if focus == 'vs':
            self.vs.draw()
        elif focus == 'ec':
            self.ec.draw()
        elif focus == 'on':
            self.on.draw()

    def scale(self, factor):
        '''
        A method to scale the eye_coordinates down so we can capture the
            movie frames using psychopy's super slow, memory hoggin methods.
        '''
        self.streak[0]      *= factor
        self.streak[1]      *= factor
        self.cup[0]         *= factor
        self.cup[1]         *= factor
        self.cup[2]         *= factor
        self.nerve[0]       *= factor
        self.nerve[1]       *= factor
        self.nerve[2]       *= factor

    def write_eye_cup_file(self, filename='eye_cup.txt'):
        '''
        Writes eye_cup to a file.
        '''
        try:
            ofile = open(filename, 'w')
        except:
            print 'Cannot open %s for writing, so I am writing to ./eye_cup.txt instead.' % (filename) 
            ofile = open('eye_cup.txt','w')

        streak = self.streak
        cup    = self.cup
        nerve  = self.nerve

        ofile.write('# These values describe how the eye cup looks relative to the screen.\n')
        ofile.write('# position and orientation of the visual streak\n')
        ofile.write('streak %d %d %3.3f\n' % (streak[0],streak[1],streak[2]))
        ofile.write('# position and diameter of the eye cup (in pixels)\n')
        ofile.write('cup %d %d %d\n' % (cup[0],cup[1],cup[2]))
        ofile.write('# position and diameter of the optic nerve (in pixels)\n')
        ofile.write('nerve %d %d %d\n' % (nerve[0],nerve[1],nerve[2]))
        ofile.close()
        
def read_eye_cup_file(filename='eye_cup.txt'):
    '''
    Reads in eye cup info from a file.
    Returns:
        streak
        cup
        nerve
    As needed for Eye_cup.__init__()
    '''
    from genutils.io import from_config
    eye_file = None
    try:
        eye_file = open(filename)
    except:
        pass

    if eye_file != None:
        # set values based on coordinate file
        streak      = from_config('streak',      filename)
        cup         = from_config('cup',         filename)
        nerve       = from_config('nerve',       filename)
    else:
        streak      = [0, 0, 0.0]
        cup         = [0, 0, 400]
        nerve       = [0, 0, 40]
        
    return streak, cup, nerve

        
        
